package com.krtech.wecard.module.oauth2;

import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.oauth2.SaOAuth2Manager;
import cn.dev33.satoken.oauth2.exception.SaOAuth2Exception;
import cn.dev33.satoken.oauth2.logic.SaOAuth2Template;
import cn.dev33.satoken.oauth2.model.CodeModel;
import cn.dev33.satoken.oauth2.model.SaClientModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.strategy.SaStrategy;
import cn.dev33.satoken.util.SaFoxUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.krtech.wecard.config.WecardRedisTemplate;
import com.krtech.wecard.module.oauth2.vo.ClientSecretVo;
import com.krtech.wecard.module.pub.entity.ApplicationManagement;
import com.krtech.wecard.module.pub.service.ApplicationManagementService;
import com.krtech.wecard.module.sys.entity.SysKeyValue;
import com.krtech.wecard.module.sys.enums.SysEnums;
import com.krtech.wecard.module.sys.service.SysKeyValueService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Sa-Token OAuth2.0 整合实现
 */
@Component
@Slf4j
public class SaOAuth2TemplateImpl extends SaOAuth2Template {

    @Autowired
    private SysKeyValueService keyValueService;

    @Autowired
    private ApplicationManagementService applicationManagementService;

    @Autowired
    private WecardRedisTemplate redisTemplate;

    public List<ClientSecretVo> getClientAndSecretList(){
        List<ApplicationManagement> list = applicationManagementService.list();

        List<ClientSecretVo> applicationClientSecretList = new ArrayList<>();

        if (list.size() > 0){
            applicationClientSecretList = list.stream().map(applicationManagement -> {
                ClientSecretVo clientSecretVo = new ClientSecretVo();
                clientSecretVo.setClientId(applicationManagement.getAppKey());
                clientSecretVo.setSecretId(applicationManagement.getAppSecret());
                return clientSecretVo;
            }).collect(Collectors.toList());
        }


        SysKeyValue globalKeyAndSecret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.GLOBAL_KEY_AND_SECRET));

        if (globalKeyAndSecret != null){
            ClientSecretVo clientSecretVo = new ClientSecretVo();
            clientSecretVo.setClientId(globalKeyAndSecret.getSysKey());
            clientSecretVo.setSecretId(globalKeyAndSecret.getSysValue());
            applicationClientSecretList.add(clientSecretVo);
        }

        return applicationClientSecretList;
    }

    /**
     * 根据id获取Client信息
     * @param clientId 应用id
     * @return ClientModel
     */
    @Override
    public SaClientModel getClientModel(String clientId) {
        //判断是否合法
        List<ClientSecretVo> clientAndSecretList = getClientAndSecretList();
        List<ClientSecretVo> clientSecretVos = clientAndSecretList.stream().filter(one -> one.getClientId().equals(clientId)).collect(Collectors.toList());
        if (clientSecretVos.size() > 0){
            ClientSecretVo clientSecretVo = clientSecretVos.get(0);

            ApplicationManagement applicationManagement = applicationManagementService.getOne(new QueryWrapper<ApplicationManagement>().eq(ApplicationManagement.COL_APP_KEY, clientId));


            SaClientModel saClientModel = new SaClientModel();
            saClientModel.setClientId(clientId);
            saClientModel.setClientSecret(clientSecretVo.getSecretId());
            saClientModel.setIsCode(true);
            if (applicationManagement != null){
                saClientModel.setAllowUrl(applicationManagement.getBusinessDomain());
            }

            return saClientModel;
        }
        return null;
    }

    /**
     * 重写 Access-Token 生成策略：复用登录会话的Token
     * @param clientId
//     * @param loginId
//     * @param scope
     * @return
     */
//    @Override
//    public String randomAccessToken(String clientId, Object loginId, String scope) {
//        String tokenValue = StpUtil.createLoginSession(loginId);
//        return tokenValue;
//    }

    @Override
    public CodeModel checkGainTokenParam(String code, String clientId, String clientSecret, String redirectUri) {

        // 校验：Code是否存在
        CodeModel cm = getCode(code);
        log.info("签发code:"+code);
        log.info("签发code存储对象:"+JSONUtil.toJsonStr(cm));

        log.info("code换token给到clientId:"+clientId);
        log.info("code换token给到clientSecret:"+clientSecret);
        log.info("code换token给到redirectUri:"+clientId);

        //判断是全局clientId，是就直接放过
//        SysKeyValue globalKeyAndSecret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
//                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.GLOBAL_KEY_AND_SECRET));
//        if (clientId.equals(globalKeyAndSecret.getSysKey())){
//            return cm;
//        }


        SaOAuth2Exception.throwBy(cm == null, "无效code: " + code);

        // 校验：ClientId是否一致
        SaOAuth2Exception.throwBy(cm.clientId.equals(clientId) == false, "无效client_id: " + clientId);

        // 校验：Secret是否正确
        String dbSecret = checkClientModel(clientId).clientSecret;
        SaOAuth2Exception.throwBy(dbSecret == null || dbSecret.equals(clientSecret) == false, "无效client_secret: " + clientSecret);

        // 如果提供了redirectUri，则校验其是否与请求Code时提供的一致
        if(SaFoxUtil.isEmpty(redirectUri) == false) {
            SaOAuth2Exception.throwBy(redirectUri.equals(cm.redirectUri) == false, "无效redirect_uri: " + redirectUri);
        }

        // 返回CodeMdoel
        return cm;
    }

    @Override
    public void checkRightUrl(String clientId, String url) {
        // 1、是否是一个有效的url
        if(SaFoxUtil.isUrl(url) == false) {
            throw new SaOAuth2Exception("无效redirect_url：" + url);
        }

        // 2、截取掉?后面的部分
        int qIndex = url.indexOf("?");
        if(qIndex != -1) {
            url = url.substring(0, qIndex);
        }

        // 3、是否在[允许地址列表]之中
        List<String> allowList = SaFoxUtil.convertStringToList(checkClientModel(clientId).allowUrl);

        //判断是全局clientId，是就直接放过
        SysKeyValue globalKeyAndSecret = keyValueService.getOne(new QueryWrapper<SysKeyValue>()
                .eq(SysKeyValue.COL_SYS_NAME, SysEnums.GLOBAL_KEY_AND_SECRET));
        if (clientId.equals(globalKeyAndSecret.getSysKey())){
            return;
        }

        boolean exceptionFlag = true;
        if (allowList.size() > 0 ){
            for (String allowUrl:allowList){
                if (url.startsWith(allowUrl)){
                    exceptionFlag = false;
                }
            }
        }

        if (exceptionFlag){
            throw new SaOAuth2Exception("非法redirect_url：" + url);
        }

    }

    /**
     * 根据ClientId 和 LoginId 获取openid
     *
     * @param clientId
     * @param loginId
     * @return
     */
    @Override
    public String getOpenid(String clientId, Object loginId) {
        // 此为模拟数据，真实环境需要从数据库查询
        return "gr_SwoIN0MC1ewxHX_vfCW3BothWDZMMtx__";
    }


//    @Override
//    public CodeModel getCode(String code) {
//        if(code == null) {
//            return null;
//        }
////        return (CodeModel) SaManager.getSaTokenDao().getObject(splicingCodeSaveKey(code));
//        String str = redisTemplate.get("wecard:oauth2:code:"+code);
//        return JSONUtil.toBean(str,CodeModel.class);
//    }
//
//    @Override
//    public void saveCode(CodeModel c) {
//        if(c == null) {
//            return;
//        }
////        SaManager.getSaTokenDao().setObject(splicingCodeSaveKey(c.code), c, 300);
//        redisTemplate.set("wecard:oauth2:code:"+c.getCode(), JSONUtil.toJsonStr(c),300);
//    }
}
